/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Ucop/Data/Rel/Session.h"
#include <cassert>
#include <cstdarg>
#include "Ucop/System/LockGuard.h"
#include "Ucop/Util/String.h"
#include "Ucop/Data/Rel/SqlConnection.h"
#include "Ucop/Data/Rel/SqlConnectionFactory.h"
#include "Ucop/Data/Rel/SqlDelayThread.h"
#include "Ucop/Data/Rel/SqlOperation.h"
using Ucop::System::LockGuard;
using Ucop::System::Mutex;


namespace Ucop {
namespace Data {
namespace Rel {

    Session::Session(void)
        : _MyallowAsyncTrans(true)
        , _MySyncConns()
        , _MySyncConnUseIndex(-1)
		, _MySyncConnsLock()
        , _MyAsyncConn()
        , _MystmtRegistry()
        , _MystmtRegistryLock()
        , _MystmtIndex(0)
        , _MyresultQueue(new SqlResultQueue)
        , _MydelayThread()
        , _MyerrHandler(NULL)
    {

    }

    Session::~Session(void)
    {
        _Uninitialize();
    }

    bool Session::Open(const std::string& _Key, int _QueryConns, 
        const std::string& _Host, const std::string& _Port, 
        const std::string& _User, const std::string _Password, 
        const std::string& _Db)
    {
        /* Must set error handler first */
        UCOP_ASSERT(_MyerrHandler != NULL);

        /* Create connections */
        _QueryConns = UCOP_MAX(_QueryConns, 1);
        for (int _Nconn = 0; _Nconn < _QueryConns; ++_Nconn)
        {
            SqlConnection* _Conn = SqlConnectionFactory::Create(this, _Key, _Host, _Port, _User, _Password, _Db);
            if (!_Conn) {
                return false;
            }
            _MySyncConns.push_back(_Conn);
        }

        _MyAsyncConn.reset(SqlConnectionFactory::Create(this, _Key, _Host, _Port, _User, _Password, _Db));
        if (!_MyAsyncConn.get()) {
            return false;
        }

        /* Create delay thread */
        _MydelayThread.reset(new SqlDelayThread(this, _MyAsyncConn.get()));
        if (!(_MydelayThread.get() && _MydelayThread->Start())) {
            return false;
        }

        return true;
    }

    void Session::Close(void)
    {
        /* Close delay thead first */
        if (_MydelayThread.get()) {
            _MydelayThread->Stop();
            _MydelayThread.reset();
        }

        /* Close connections */
        if (_MyAsyncConn.get()) {
            _MyAsyncConn->Close();
            _MyAsyncConn.reset();
        }
        while (!_MySyncConns.empty()) {
            std::auto_ptr<SqlConnection> _Conn(_MySyncConns.back());
            _MySyncConns.pop_back();

            _Conn->Close();
        }
    }

    void Session::EscapeString(std::string& _Sql)
    {
        _MySyncConns[0]->EscapeString(_Sql);
    }

    void Session::Ping(void)
    {
        std::vector<SqlConnection*>::iterator _Iter = _MySyncConns.begin();
        for (; _Iter != _MySyncConns.end(); ++_Iter) {
            (*_Iter)->Ping();
        }
    }

    std::string Session::GetServerInfo(void)
    {
        return _MySyncConns[0]->GetServerInfo();
    }

    std::string Session::GetClientInfo(void)
    {
        return _MySyncConns[0]->GetClientInfo();
    }

    void Session::SetErrorHandler(ERROR_HANDLER _Handler)
    {
        UCOP_ASSERT(_MyerrHandler == NULL && _Handler != NULL);
        _MyerrHandler = _Handler;
    }

    QueryResult* Session::PQuery(const char* _Sql, ...)
    {
        char _Lsql[MAX_QUERY_LENGTH];

        std::va_list _Arg;
        va_start(_Arg, _Sql);
        int _Ret = Util::vsnprintf(_Lsql, MAX_QUERY_LENGTH, _Sql, _Arg);
        va_end(_Arg);

        if (_Ret == -1) {
            _MyerrHandler(-1, "SQL Query truncated (and not execute) for format: %s\n", _Sql);
            return NULL;
        }
		return _GetConnection()->Query(_Lsql);
    }

    bool Session::PExecute(const char* _Sql, ...)
    {
        char _Lsql[MAX_QUERY_LENGTH];

        std::va_list _Arg;
        va_start(_Arg, _Sql);
        int _Ret = Util::vsnprintf(_Lsql, MAX_QUERY_LENGTH, _Sql, _Arg);
        va_end(_Arg);

        if (_Ret == -1)
        {
            _MyerrHandler(-1, "SQL Query truncated (and not execute) for format: %s\n", _Sql);
            return false;
        }
		return _GetConnection()->Execute(_Lsql);
    }

    SqlStatement Session::CreateStatement(SqlStatementID& _ID, const char* _Format)
    {
        int _Id = -1;
        if (!_ID.IsInitialized())
        {
            std::string _SzFmt(_Format);
            std::size_t _Params = std::count(_SzFmt.begin(), _SzFmt.end(), '?');
            UCOP_LOCK_GURAD_ASSERT(_MystmtRegistryLock);
            PreparedStmtRegistry::const_iterator _Iter = _MystmtRegistry.find(_SzFmt);
            if (_Iter == _MystmtRegistry.end())
            {
                _Id = ++_MystmtIndex;
                _MystmtRegistry[_SzFmt] = _Id;
            }
            else {
                _Id = _Iter->second;
            }

            _ID._Init(_Id, _Params);
        }
        return SqlStatement(_ID, *this);
    }

    bool Session::ExecuteStatement(const SqlStatementID& _ID, SqlStmtParameters* _Params)
    {
        if (!_MyallowAsyncTrans)
        {
            return DirectExecuteStatement(_ID, _Params);
        }
        _MydelayThread->Delay(new SqlPreparedRequest(_ID.id(), _Params));
        return true;
    }

    bool Session::DirectExecuteStatement(const SqlStatementID& _ID, SqlStmtParameters* _Params)
    {
        UCOP_ASSERT(_Params);
        std::auto_ptr<SqlStmtParameters> _P(_Params);
        SqlConnection::Lock _Guard(_MyAsyncConn.get());
        return _Guard->ExecuteStmt(_ID.id(), *_Params);
    }

    std::string Session::GetStatementString(const int _ID)
    {
        UCOP_LOCK_GURAD_ASSERT(_MystmtRegistryLock);

        if (_ID == -1 || _ID > _MystmtIndex) {
            return std::string();
        }

        PreparedStmtRegistry::const_iterator _Iter = _MystmtRegistry.begin();
        PreparedStmtRegistry::const_iterator _IterLast = _MystmtRegistry.end();
        for (; _Iter != _IterLast; ++_Iter)
        {
            if (_Iter->second == _ID)
            {
                return _Iter->first;
            }
        }
        return std::string();
    }

    void Session::ProcessAsyncResultQueue(void)
    {
        if (_MyresultQueue.get()) {
            _MyresultQueue->Update();
        }
    }

    SqlConnection* Session::_GetConnection(void)
    {
		UCOP_LOCK_GURAD_ASSERT(_MySyncConnsLock);
        if (++_MySyncConnUseIndex >= (int)_MySyncConns.size()) {
            _MySyncConnUseIndex = 0;
        }
        return _MySyncConns[_MySyncConnUseIndex];
    }

    void Session::_Uninitialize(void)
    {
        Close();

        /* Clear async result queue */
        if (_MyresultQueue.get())
        {
            IQueryCallback* _Callback = NULL;
            while (_MyresultQueue->Next(_Callback))
            {
                delete _Callback;
            }
        }
    }
}}}